package com.hccl.service.update;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hccl.config.Constants;
import com.hccl.exception.updateException;
import com.hccl.exception.updateFileOperateException;
import com.hccl.mongoEntities.*;
import com.hccl.util.FileOperations;
import org.apache.commons.io.FileUtils;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * 更新流程文件中常用的操作集合
 */
@Component
public class UpdateOptions {
    @Autowired
    Datastore datastore;

    private final static Logger LOG = LoggerFactory.getLogger(UpdateOptions.class);

    public boolean updateCorpusAndProcessFromDB() throws updateException, Exception {
        boolean updateSuccessed = false;
        //开始更新corpus
        File corpusFile = new File(Constants.corpusPath);
        if (corpusFile.exists()) {
            try {
                FileOperations.deleteDirectory(Constants.corpusPath);
                LOG.info("【删除当前corpus】");
            } catch (Exception e) {
                LOG.error("【删除corpus文件夹时有异常】" + e);
                throw new updateException("【删除corpus文件夹时有异常】");
            }
            corpusFile.mkdir();
        }
        try {
            DB2Corpus();
        } catch (updateException e) {
            LOG.error("【更新corpus失败】");
            throw new updateException("【更新corpus失败】");
        }
        LOG.info("【corpus更新成功！】");

        //更新json文件
        File nodeInfoFile = new File(Constants.nodeInfoPath);
        File processFile = new File(Constants.PROCESSPATH);
        if (nodeInfoFile.exists()) {
            try {
                FileOperations.deleteDirectory(Constants.nodeInfoPath);
            } catch (Exception e) {
                LOG.error("【删除json文件夹时异常】" + e);
                throw new updateException("【删除json文件夹时异常】");
            }
            nodeInfoFile.mkdir();
            processFile.mkdir();
            FileOperations.cpFile("lib/nodeInfobak/nodeInfo.json", Constants.jsonPath);
        }
        createJsonfromDB();
        LOG.info("【流程更新成功！】");
        deleteProcessFiles();
        LOG.info("【删除备份文件！】");
        updateSuccessed = true;
        return updateSuccessed;
    }


    /**
     * 更新corpus文件
     *
     * @throws updateException
     */
    private void DB2Corpus() throws updateException {
        try {
            //nodeCorpus
            Query<NavigationEntity> queryNavi = datastore.createQuery(NavigationEntity.class);
            for (NavigationEntity navi : queryNavi) {
//            获取到节点的语料并且存到resources/Corpus
                if (!navi.getId().contains("##") && !navi.getId().contains("root")) {
                    List<String> corpus_node = navi.getCorpus();
                    OutputStreamWriter fw_navi = new OutputStreamWriter(new FileOutputStream
                            (Constants.corpusPath + "stateNode@" + navi.getId(), false), "UTF-8");
                    for (String c : corpus_node) {
                        //用于编辑距离计算的文本需要去标点去空格
                        c = c.replaceAll("[\\pP\\p{Punct}]", "").replace(" ", "");
                        fw_navi.write(c + "\n");
                    }
                    fw_navi.close();
                }
            }

            //commandCorpus
            Query<CommandEntity> queryCommand = datastore.createQuery(CommandEntity.class);
            for (CommandEntity comm : queryCommand) {
//            获取到节点的语料并且存到resources/Corpus
                List<String> corpus_comm = comm.getCorpus();
                OutputStreamWriter fw_comm = new OutputStreamWriter(new FileOutputStream
                        (Constants.corpusPath + "command@" + comm.getName(), false), "UTF-8");
                for (String c : corpus_comm) {
                    c = c.replaceAll("[\\pP\\p{Punct}]", "").replace(" ", "");
                    fw_comm.write(c + "\n");
                }
                fw_comm.close();
            }
        } catch (Exception e) {
            throw new updateException("更新时生成courpus文件出错。系统尝试自动恢复...");
        }
    }


    /**
     * description:从数据库中更新本地Json配置文件
     *
     * @return null
     * @Param: null
     */
    private void createJsonfromDB() throws updateException {
        try {

            //写节点
            Query<NavigationEntity> q = datastore.createQuery(NavigationEntity.class).field("_id").equal("root");
            NavigationEntity root = q.get();

            if (root != null) {
                //设置节点(用递归的方法设置节点)
                setChildNavigationEntity(root);
            }
            writeJsonStringToFile(Constants.nodeInfoPath + "stateNode.json", root);

            //写processes
            Query<ProcessEntity> processes = datastore.createQuery(ProcessEntity.class);
            for (ProcessEntity p : processes) {
                writeJsonStringToFile(Constants.PROCESSPATH + p.getName() + ".json", p);
            }

            //写api
            List<APIsEntity> apiArrayList = new ArrayList<APIsEntity>();
            Query<APIsEntity> apilists = datastore.createQuery(APIsEntity.class);
            for (APIsEntity p : apilists) {
                apiArrayList.add(p);
                writeJsonStringToFile(Constants.nodeInfoPath + "apiList.json", apiArrayList);
            }

            //写config
            Query<ConfigEntity> configQuery = datastore.createQuery(ConfigEntity.class);
            ConfigEntity config = configQuery.get();
            writeJsonStringToFile(Constants.nodeInfoPath + "config.json", config);

//        //写params
            List<GrammarVariableEntity> paramsList = new ArrayList<GrammarVariableEntity>();
            Query<GrammarVariableEntity> paramsQuery = datastore.createQuery(GrammarVariableEntity.class);
            for (GrammarVariableEntity param :
                    paramsQuery) {
                paramsList.add(param);
            }
            writeJsonStringToFile(Constants.nodeInfoPath + "params.json", paramsList);
        } catch (Exception e) {
            throw new updateException("更新时生成nodeInfo文件出错。系统尝试自动恢复...");
        }
    }

    /**
     * description: 备份流程文件
     * src -> (src+bak)
     */
    public void backupProcessFile() throws updateFileOperateException {
        //备份完毕
        FileOperations.reNameFile(Constants.corpusPath);
        FileOperations.reNameFile(Constants.nodeInfoPath);

    }

    /**
     * description: 更新前的文件备份
     *
     * @return null
     * @Param: null
     */
    public void deleteProcessFiles() throws Exception {
        //备份完毕
        FileOperations.deleteDirectory(processPath(Constants.nodeInfoPath));
        FileOperations.deleteDirectory(processPath(Constants.corpusPath));
    }


    public void recoverProcessJson() throws IOException {
        FileUtils.copyDirectory(new File(processPath(Constants.nodeInfoPath)), new File(Constants.nodeInfoPath));
    }

    public String processPath(String path) {
        String newPath = path.substring(0, path.length() - 1) + "bak";
        return newPath;
    }

    private void writeJsonStringToFile(String path, Object object) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = mapper.writeValueAsString(object);
        OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(path, false), "UTF-8");
        fw.write(jsonString);
        //System.out.println(jsonString);
        fw.close();
    }

    private void setChildNavigationEntity(NavigationEntity root) {
        root.setCorpus(null);
        List<NavigationEntity> children = root.getSubStateNode();
        if (children.size() == 0) {
            return;
        }
        for (int i = 0; i < children.size(); i++) {
            String name = children.get(i).getId();
            Query<NavigationEntity> q = datastore.createQuery(NavigationEntity.class).field("_id").equal(name);
            NavigationEntity child = q.get();
            root.getSubStateNode().set(i, child);
            setChildNavigationEntity(child);
        }

    }
}
